package base.reflect.proxy;

import base.monad.Try;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public interface IAspect {
    void before();
    void after();

    /**
     * 使用monad的能力，不再过多地关注异常的情况
     */
    @SuppressWarnings("all")
    static <T> T getProxy(Class<T> cls, String ... aspects) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        List<Try<IAspect>> aspectInstances = Arrays.stream(aspects)
                .map(name -> Try.ofFailable(() -> {
                    var clazz = Class.forName(name);
                    return (IAspect) clazz.getConstructor().newInstance();
                }))
                .filter(Try::isSuccess)
                .collect(Collectors.toList());

        var instance = cls.getConstructor().newInstance();
        return (T) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), (proxy, method, args) -> {
            for (var aspect : aspectInstances) aspect.get().before();
            // 基于哪个实例调用的，需要传入实例对象
            var result =  method.invoke(instance);
            for (var aspect : aspectInstances) aspect.get().after();
            return result;
        });
    }
}
